package com.cl.pile.rule.utils;

import java.sql.Date;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.LinkedList;
import java.util.List;
import java.util.Objects;
import java.util.Stack;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * @author chenyitao
 * @date 2021/4/20
 */
public class Calculator {

    private enum Operator {
        ADD("+", 10), SUBTRACT("-", 10), MULTIPLY("*", 20), DIVIDE("/", 20),
        PARENTHESIS_LEFT("(", 100), PARENTHESIS_RIGHT(")", 100);
        private final String operator;
        private final int priority;

        Operator(String operator, int priority) {
            this.operator = operator;
            this.priority = priority;
        }
    }

    private enum Operand {
        ONE("1"), TWO("2"), THREE("3"), FOUR("4"), FIVE("5"), SIX("6"),
        SEVEN("7"), EIGHT("8"), NINE("9"), ZERO("0"), POINT(".");
        private final String operand;

        Operand(String operand) {
            this.operand = operand;
        }
    }


    private static Operator getOperator(String str) {
        for (Operator op : Operator.values()) {
            if (str.equals(op.operator)) {
                return op;
            }
        }
        return null;
    }

    private static Operand getOperand(String str) {
        for (Operand op : Operand.values()) {
            if (str.equals(op.operand)) {
                return op;
            }
        }
        return null;
    }

    private static List<String> resolveExpr(String exp) {
        List<String> list = new LinkedList<>();
        String temp = "";
        exp = exp.replace(" ", "");
        for (int i = 0; i < exp.length(); i++) {
            String str = exp.substring(i, i + 1);
            Operator op = getOperator(str);
            Operand od = getOperand(str);
            if (op != null) {
                if (!temp.isEmpty()) {
                    list.add(temp);
                    temp = "";
                }
                list.add(str);
            } else if (od != null) {
                temp += str;
            } else {
                throw new RuntimeException("表达式[" + str + "]非法! ");
            }
        }
        if (!temp.isEmpty()) {
            list.add(temp);
        }
        return list;
    }

    private static List<String> dealExpr(List<String> expList) {
        if (expList == null) {
            return null;
        }

        List<String> list = new LinkedList<>();
        Stack<String> stack = new Stack<>();
        for (String str : expList) {
            Operator op = getOperator(str.substring(0, 1));
            Operand od = getOperand(str.substring(0, 1));
            if (od != null) {
                //操作数直接入队列
                list.add(str);
            } else if (op != null) {
                if (Operator.PARENTHESIS_LEFT.equals(op)) {
                    //左括号入栈
                    stack.push(str);
                } else if (Operator.PARENTHESIS_RIGHT.equals(op)) {
                    //右括号: 循环将栈顶的运算符取出并存入队列，直到取出左括号
                    while (true) {
                        if (stack.empty()) {
                            System.out.println("缺少左括号! ");
                            return null;
                        } else if (Operator.PARENTHESIS_LEFT.operator.equals(stack.peek())) {
                            stack.pop();
                            break;
                        } else {
                            list.add(stack.pop());
                        }
                    }
                } else {
                    //非括号类运算符
                    if (!stack.empty()) {
                        Operator topOp = getOperator(stack.peek());
                        //当前运算符优先级大于栈顶运算符优先级，或者栈顶为左括号时，当前运算符直接入栈
                        if (op.priority <= Objects.requireNonNull(topOp).priority
                                && !Operator.PARENTHESIS_LEFT.equals(topOp)) {
                            list.add(stack.pop());
                        }
                    }
                    stack.push(str);
                }
            }
        }
        while (!stack.empty()) {
            String str = stack.peek();
            if (Operator.PARENTHESIS_LEFT.operator.equals(str)) {
                System.out.println("缺少右括号! ");
                return null;
            } else {
                list.add(stack.pop());
            }
        }
        return list;
    }


    private static String operation(String x, String y, Operator op) {
        double a = 0.0;
        double b = 0.0;
        try {
            a = Double.parseDouble(x);
            b = Double.parseDouble(y);
        } catch (NumberFormatException e) {
            System.out.println("操作数非法! ");
            e.printStackTrace();
        }

        switch (op) {
            case ADD:
                return String.valueOf(a + b);
            case SUBTRACT:
                return String.valueOf(a - b);
            case MULTIPLY:
                return String.valueOf(a * b);
            case DIVIDE:
                return String.valueOf(a / b);
            default:
                return null;
        }
    }


    public static String calculate(String exp) {
        List<String> expList = dealExpr(resolveExpr(exp));
        if (expList == null) {
            return null;
        }
        Stack<String> stack = new Stack<>();
        for (String str : expList) {
            Operator op = getOperator(str.substring(0, 1));
            Operand od = getOperand(str.substring(0, 1));
            if (od != null) {
                stack.push(str);
            } else if (op != null) {
                //目前仅针对二元运算符
                String x = "";
                String y = "";
                if (!stack.empty()) {
                    y = stack.pop();
                }
                if (!stack.empty()) {
                    x = stack.pop();
                }
                if (!x.isEmpty()) {
                    String result = operation(x, y, op);
                    if (result == null) {
                        return null;
                    }
                    stack.push(result);
                } else {
                    return null;
                }
            }
        }
        return stack.pop();
    }

    public static String calculateDate(String exp) {

        Matcher matcher = getDateMatcherFromString(exp);
        String date = null;
        if (matcher.find()) {
            date = matcher.group(0);
        }

        assert date != null;
        exp = exp.replace(date,"").replace(" ","");
        if (exp.contains(":")) {
            exp = exp.substring(10);
        }
        if (exp.isEmpty()) {
            return date;
        }
        char op = exp.charAt(0);
        exp = exp.replaceFirst("" + op,"");
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd");


        long dateTimestamp = 0L;
        try {
            dateTimestamp = simpleDateFormat.parse(date).getTime();
        } catch (ParseException e) {
            e.printStackTrace();
        }
        if (matcher.find()) {
            long dateTimestamp1 = 0L;
            String date1 = matcher.group(0);
            try {
                dateTimestamp1 = simpleDateFormat.parse(date1).getTime();
            } catch (ParseException e) {
                e.printStackTrace();
            }
            //                case '+':
            //                    return simpleDateFormat.format(new Date(dateTimestamp + dayTimestamp));
            if (op == '-') {
                return String.valueOf((dateTimestamp - dateTimestamp1) / 1000 / 60 / 60 / 24);
            }

            throw new RuntimeException("未识别运算符:" + op + ", date1:" + dateTimestamp + ", date2:"+ dateTimestamp1 +"  , exp:" + exp);
        }
        long day = Long.parseLong(exp);
        long dayTimestamp = day * 1000 * 60 * 60 * 24;
        switch (op) {
            case '+':
                return simpleDateFormat.format(new Date(dateTimestamp + dayTimestamp));
            case '-':
                return simpleDateFormat.format(new Date(dateTimestamp - dayTimestamp));
            default:
                throw new RuntimeException("未识别运算符:" + op + ", date1:" + dateTimestamp + ", date2:"+ dayTimestamp +"  , exp:" + exp);
        }
    }
    public static Matcher getDateMatcherFromString(String str) {
        String regex = "[0-9]{4}[-][0-9]{1,2}[-][0-9]{1,2}";
        Pattern pattern = Pattern.compile(regex);
        return pattern.matcher(str);
    }
}
